import json
import logging
from json import loads
from re import match

from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http.response import (HttpResponseBadRequest,
                                  HttpResponseForbidden, JsonResponse)
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views import View
from django_redis import get_redis_connection

from apps.carts.utlis import merge_cookies_to_redis
from apps.goods.models import SKU
from apps.users.models import User
from utils.response_code import RETCODE
from utils.views import LoginRequiredJSONMixin

from .models import Address
from .utils import activate_email_url, check_email_activate_token

# Create your views here.

logger = logging.getLogger(__name__)


class RegisterView(View):
    """用户注册"""

    def get(self, request):
        """返回注册界面"""
        return render(request, 'register.html')

    def post(self, request):
        """验证前端提交的用户名、密码、手机号，保存到数据库，返回响应信息"""
        # 获取数据
        data = request.POST
        username = data.get('username')
        password = data.get('password')
        password2 = data.get('password2')
        mobile = data.get('mobile')
        allow = data.get('allow')
        # all()判断所有元素是否为true，只要有一个为false，就返回false
        if not all([username, password, password2, mobile]):
            return HttpResponseBadRequest('提交的用户信息不完整')
        # 判断用户名是否符合规则
        if not match(r'^[a-zA-Z0-9_]{5,20}$', username):
            return HttpResponseBadRequest('用户名不符合规则')
        # 判断密码是否是8-20个数字
        if not match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')
        # 判断手机号是否合法
        if not match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')
        # 判断是否勾选用户协议
        if allow != 'on':
            return HttpResponseBadRequest('请勾选用户协议')

        # 验证短信验证码是否正确
        sms_code_client = request.POST.get('sms_code')
        redis_conn = get_redis_connection()
        sms_code_server = redis_conn.get(mobile)
        if sms_code_server is None:
            return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})

        # 数据验证通过，保存到数据库
        try:
            user = User.objects.create_user(
                username=username, password=password, mobile=mobile)
        except Exception as e:
            logger.error(e)
            return render(request, 'register.html', {'register_errmsg': '注册异常'})
        else:
            # 注册成功，保持登陆状态,并跳转到首页
            from django.contrib.auth import login
            login(request, user)
            return redirect(reverse('contents:index'))


class UserNameCountView(View):
    """从数据库读取用户名，判断用户名是否重复"""
    # 从url中获取username参数

    def get(self, request, username):
        # 通过count函数查询该名称是否已存在，0表示不存在，1表示存在
        try:
            count = User.objects.filter(username=username).count()
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 400, 'errmsg': '数据库异常'})
        else:
            return JsonResponse({'code': '0', 'count': count})


class UserMobileCountView(View):
    """从数据库读取手机号，判断手机号是否重复"""
    # 从url中获取mobile参数

    def get(self, request, mobile):
        # 通过count函数查询该号码是否已存在，0表示不存在，1表示存在
        try:
            count = User.objects.filter(mobile=mobile).count()
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 400, 'errmsg': '数据库异常'})
        else:
            return JsonResponse({'code': '0', 'count': count})


class LoginView(View):
    def get(self, request):
        """返回登陆页面"""
        return render(request, 'login.html')

    def post(self, request):
        """实现登录逻辑"""
        # 接受参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')

        # 判断参数是否齐全
        if not all([username, password]):
            return HttpResponseBadRequest('缺少必传参数')

        # 判断用户名是否是5-20个字符
        if not match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseBadRequest('请输入正确的用户名或手机号')

        # 判断密码是否是8-20个数字
        if not match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('密码最少8位，最长20位')

        # 认证登录用户
        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})

        # 实现状态保持
        login(request, user)
        # 设置状态保持的周期
        if remembered != 'on':
            # 没有记住用户：浏览器关闭就过期
            request.session.set_expiry(0)
        else:
            # 记住用户：None表示两周后过期
            request.session.set_expiry(None)

            # 跳转到next参数指定的页面
            next = request.GET.get('next')
            if next:
                response = redirect(next)
            else:
                response = redirect(reverse('contents:index'))

            # 合并cookies和redis中的购物车信息
            response = merge_cookies_to_redis(request, user, response)

            # 设置cookie
            response.set_cookie('username', user.username, max_age=14*24*3600)
            return response


class LogoutView(View):
    """退出登录"""

    def get(self, request):
        # 使用django自带的方法退出登录
        from django.contrib.auth import logout
        logout(request)

        # 跳转到指定页面
        response = redirect(reverse('contents:index'))
        response.delete_cookie('username')
        return response


# 使用系统自带验证方法验证是否已经登陆，由于LoginRequiredMixin没有dispatch方法，所以依旧需要继承View
class UserCenterInfoView(LoginRequiredMixin, View):
    def get(self, request):
        # request.user 就是登陆用户实例对象
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_activate': request.user.email_activate
        }
        return render(request, 'user_center_info.html', context)


# 导入返回json数据的自定义LoginRequiredJSONMixin
class EmailView(LoginRequiredJSONMixin, View):
    """添加邮箱"""

    def put(self, request):
        # put提交的参数保存在body中
        data = loads(request.body.decode())
        email = data.get('email')
        # 检查邮箱格式
        if not match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '邮箱格式错误'})
        try:
            # 保存到数据库中
            request.user.email = email
            request.user.save()
        except Exception:
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '数据库保存失败'})
        else:
            # 激活邮箱的url
            verify_url = activate_email_url(email, request.user.id)
            # 使用celery异步发送激活邮件
            from celery_tasks.email_send.tasks import send_activate_mail
            send_activate_mail.delay(email, verify_url)

            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})


class EmailActivateView(View):
    """激活邮箱"""

    def get(self, request):
        token = request.GET.get('token')
        if token is None:
            return HttpResponseBadRequest('缺少必要参数')
        # 根据参数查询用户
        user = check_email_activate_token(token)
        if user is None:
            return HttpResponseBadRequest('没有此用户')
        # 修改用户信息
        user.email_activate = True
        user.save()
        # 跳转到个人中心
        return redirect(reverse('user:center'))


class AddressView(View):
    """显示用户保存的地址信息"""

    def get(self, request):
        addresses = Address.objects.filter(user=request.user, is_deleted=False)

        # 转换为列表字典，以便Vue渲染
        address_dict_list = []
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "province_id": address.province_id,
                "city": address.city.name,
                "city_id": address.city_id,
                "district": address.district.name,
                "district_id": address.district_id,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)

        context = {
            'addresses': address_dict_list,
            'default_address_id': request.user.default_address_id,
        }
        return render(request, 'user_center_site.html', context=context)


class CreateAddressView(View):
    """新增收货地址"""

    def post(self, request):
        # 判断用户的地址数量是否超过20个
        addr_count = request.address.all().count()
        if addr_count >= 20:
            return JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': "地址数量已达到最大上限"})

        # 接收前端提交的数据
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return HttpResponseBadRequest('缺少必传参数')
        if not match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('参数mobile有误')
        if tel:
            if not match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return HttpResponseBadRequest('参数tel有误')
        if email:
            if not match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return HttpResponseBadRequest('参数email有误')

        # 数据入库
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as exc:
            logger.error(exc)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        # 判断用户是否已有默认地址
        if request.user.default_address is None:
            # 将本次提交的地址设为默认
            request.user.default_address = address
            request.user.save()

        # 返回响应
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


class UpdateAddressView(View):
    """修改和删除用户地址信息"""

    def put(self, request, address_id):
        """修改地址"""
        # 获取地址信息
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验地址信息
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return HttpResponseBadRequest('缺少必传参数')
        if not match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('参数mobile有误')
        if tel:
            if not match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return HttpResponseBadRequest('参数tel有误')
        if email:
            if not match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return HttpResponseBadRequest('参数email有误')

        # 更新数据
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception:
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改地址失败'})

        # 返回更新后的地址
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '地址修改成功', 'address': address_dict})

    def delete(self, request, address_id):
        """删除地址"""
        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)

            # 将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        # 响应删除地址结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})


class DefaultAddressView(LoginRequiredJSONMixin, View):
    """设置默认地址"""

    def put(self, request, address_id):
        """设置默认地址"""
        try:
            # 接收参数,查询地址
            address = Address.objects.get(id=address_id)

            # 设置地址为默认地址
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})

        # 响应设置默认地址结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


class UpdateTitleAddressView(LoginRequiredJSONMixin, View):
    """设置地址标题"""

    def put(self, request, address_id):
        """设置地址标题"""
        # 接收参数：地址标题
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        try:
            # 查询地址
            address = Address.objects.get(id=address_id)

            # 设置新的地址标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})

        # 4.响应删除地址结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})


class ChangePasswordView(LoginRequiredMixin, View):
    """修改密码"""

    def get(self, request):
        """展示密码修改页面"""
        return render(request, 'user_center_pass.html')

    def post(self, request):
        """修改密码"""
        # 接收参数
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        new_password2 = request.POST.get('new_password2')

        # 验证参数
        if not all([old_password, new_password, new_password2]):
            return HttpResponseForbidden('缺少必传参数')
        if not match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return HttpResponseBadRequest('密码最少8位，最长20位')
        if new_password != new_password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        # 验证旧密码是否正确
        if not request.user.check_password(old_password):
            return render(request, 'user_center_pass.html', {'origin_password_errmsg': '原始密码错误'})

        # 修改密码
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'change_password_errmsg': '修改密码失败'})
        # 5.退出登陆,删除登陆信息
        logout(request)
        # 6.跳转到登陆页面
        response = redirect(reverse('users:login'))

        response.delete_cookie('username')

        return response


class AddUserHistoryView(LoginRequiredJSONMixin, View):
    """添加用户浏览历史记录"""

    def post(self, request):
        """保存用户历史记录"""
        # 接收用户信息
        user = request.user
        # 接受POST提交的数据
        data = json(request.body.decode())
        sku_id = data.get('sku_id')

        # 验证信息
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code': RETCODE.NODATAERR})

        # 连接到redis
        conn = get_redis_connection('history')
        # 先删除后保存，保证了记录的顺序性
        # 如果已有旧记录，就删除
        conn.lrem('history_%s' % user.id, 0, sku_id)
        # 保存新的记录
        conn.lpush('history_%s' % user.id, sku_id)
        # 只保留5条记录
        conn.ltrim('history_%s' % user.id, 0, 4)

    def get(self, request):
        """返回用户历史记录"""
        user = request.user
        conn = get_redis_connection('history')
        # 获取Redis列表中的数据
        id_list = conn.lrange('history_%s' % user.id, 0, 4)

        # 获取id对应的商品信息，保存到字典中
        skus = {}
        for id in id_list:
            sku = SKU.objects.get(id=id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})
